package mybatis.base;

import cur.nature.framework.core.exception.SystemException;
import cur.nature.framework.core.mirco.MircoIdUtil;
import cur.nature.framework.core.util.JsonUtil;
import cur.nature.framework.core.util.ObjectUtil;
import cur.nature.framework.core.util.SnowflakeIdUtil;
import cur.nature.framework.mvc.base.BaseDao;
import cur.nature.framework.mvc.base.BasePO;
import cur.nature.framework.mvc.base.BaseVO;
import cur.nature.tinyorm.core.bean.PageBean;
import cur.nature.tinyorm.core.proxy.TemplateProxy;
import cur.nature.tinyorm.core.template.DefaultJdbcTemplate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class MybatisDao<P extends BasePO, V extends BaseVO> implements BaseDao {
    private Class<P> poClass = null;
    private Class<V> voClass = null;
    private DefaultJdbcTemplate template;
    private TemplateProxy templateProxy;
    private BaseMapper baseMapper;

    public MybatisDao(Class<P> poClass, Class<V> voClass, DefaultJdbcTemplate template,BaseMapper baseMapper) {
        this.poClass = poClass;
        this.voClass = voClass;
        this.template = template;
        this.baseMapper = baseMapper;
        this.templateProxy = this.template.getTemplateProxy();
    }

    public DefaultJdbcTemplate getTemplate() {
        return template;
    }


    public <T> PageBean<T> queryForPage(String sql, PageBean<T> pagination, Class<T> resultType) throws Exception {
        return queryForPage(sql, pagination, null, resultType);
    }

    public <T> PageBean<T> queryForPage(String sql, PageBean<T> pagination, Map<String, Object> param,
                                        Class<T> resultType) throws Exception {
        return templateProxy.queryForPage(sql, pagination, param, resultType);
    }

    public <T> PageBean<T> queryForPage(String sql, Object[] param, PageBean<T> pagination,
                                        Class<T> resultType) throws Exception {
        return queryForPage(sql, pagination, null, resultType);
    }

    public <T> List<T> queryForListCust(String sql, Class<T> resultType) throws Exception {
        return queryForListCust(sql, null, resultType);
    }

    public <T> List<T> queryForListCust(String sql, Map<String, Object> param, Class<T> resultType) throws Exception {
        return templateProxy.queryForListCust(sql, param, resultType);
    }

    public <T> List<T> queryForListJdbc(String sql, Map<String, Object> param, Class<T> resultType) throws Exception {
        return templateProxy.queryForListJdbc(sql, param, resultType);
    }

    public <T> T queryForObjectCust(String sql, Class<T> resultType) throws Exception {
        return templateProxy.queryForObjectCust(sql, resultType);
    }

    public P toPo(V vo) {
        if (vo == null) {
            return null;
        }
        return JsonUtil.parseObject(JsonUtil.toJSONString(vo), poClass);
    }

    public List<P> toPoList(List<V> voList) {
        List<P> list = new ArrayList<>();
        for (V vo : voList) {
            list.add(toPo(vo));
        }
        return list;
    }

    public V toVo(P po) {
        if (po == null) {
            return null;
        }
        return JsonUtil.parseObject(JsonUtil.toJSONString(po), voClass);
    }

    public List<V> toVoList(List<P> poList) {
        List<V> list = new ArrayList<>();
        for (P po : poList) {
            list.add(toVo(po));
        }
        return list;
    }


    public <V> V queryById(String id) throws Exception {
        Object obj = baseMapper.selectByPrimaryKey(id);
        return (V) toVo((P) obj);
    }

    public <V extends BaseVO> List<V> queryByIdList(List<Long> idList) throws Exception {
        Object obj = templateProxy.queryByIdList(poClass, idList);
        if (obj != null && ((List) obj).size() > 0) {
            return toVoList((List) obj);
        }
        return null;
    }

    protected <V extends BaseVO> List<V> queryByCond(String condition) throws Exception {
        Object obj = templateProxy.queryByCond(condition, poClass);
        if (obj != null && ((List) obj).size() > 0) {
            return toVoList((List) obj);
        }
        return null;
    }

    protected <V extends BaseVO> List<V> queryByCond(String condition, Map<String, Object> param) throws Exception {
        Object obj = templateProxy.queryByCond(condition, param, poClass);
        if (obj != null && ((List) obj).size() > 0) {
            return toVoList((List) obj);
        }
        return null;
    }

    protected <V extends BaseVO> List<V> queryByCond(String condition, Object[] param) throws Exception {
        Object obj = templateProxy.queryByCond(condition, param, poClass);
        if (obj != null && ((List) obj).size() > 0) {
            return toVoList((List) obj);
        }
        return null;
    }

    protected Long countByCond(String condition) {
        return templateProxy.countByCond(condition, poClass);
    }

    public int deleteById(Long id) {
        return deleteById(id, null);
    }

    public int deleteById(Long id, String tableName) {
        return templateProxy.deleteByIdList(Arrays.asList(id), poClass, tableName);
    }

    public int deleteByIdList(List<Long> idList) throws Exception {
        int result = templateProxy.deleteByIdList(idList, poClass);
        return result;
    }

    protected int deleteByCond(String condition) {
        return templateProxy.deleteByCond(condition, poClass);
    }

    public int save(V vo) throws Exception {
        int result = 0;
        if (vo.getOperateType() == null) {
            return 0;
        }
        // 新增
        if (vo.getOperateType() == 1) {
            P po = toPo(vo);
            po.setCreateBase();
            result += insertSelective(po);
            vo.setId(po.getId());
        }
        // 修改
        else if (vo.getOperateType() == 2) {
            result += updateByIdSelective(toPo(vo));
        }
        // 删除
        else if (vo.getOperateType() == 3) {
            result += deleteById(vo.getId());
        }
        return result;
    }

    public int saveList(List<V> list) throws Exception {
        return saveList(list, null);
    }

    public int saveList(List<V> list, String tableName) throws Exception {
        int result = 0;
        // 对批量插入进行优化
        boolean batchInsert = false;
        boolean batchUpdate = false;
        if (ObjectUtil.isNotEmpty(list) && list.size() > 100) {
            // 批量插入
            Long inserCnt = list.stream().filter(s -> s.getOperateType() == 1).count();
            if (inserCnt > 100) {
                batchInsert = true;
                List<V> insertList = list.stream().filter(s -> s.getOperateType() == 1).collect(Collectors.toList());
                List<P> insertPoList = new ArrayList<>();
                for (int i = 0; i < insertList.size(); i++) {
                    V vo = insertList.get(i);
                    P po = toPo(vo);
                    po.setCreateBase();
                    po.setId(MircoIdUtil.generateId());
                    insertPoList.add(po);
                    // 分批处理
                    if (i != 0 && ((i % 1000 == 0) || ((i + 1) == inserCnt))) {
                        templateProxy.batchInsert(insertPoList, tableName);
                        insertPoList.clear();
                    }
                }
            }

            // @TODO 批量更新, 注意: 批量更新是更新所有字段的,并且为id更新, 如果不满足业务需求,请使用其它方法
            Long updateCnt = list.stream().filter(s -> s.getOperateType() == 2).count();
            if (updateCnt > 100) {
                batchUpdate = true;
                List<V> updateList = list.stream().filter(s -> s.getOperateType() == 2).collect(Collectors.toList());
                List<P> updatePoList = new ArrayList<>();
                for (int i = 0; i < updateList.size(); i++) {
                    P po = toPo(updateList.get(i));
                    po.setModifyDate(new Date());
                    updatePoList.add(po);
                    // 分批处理
                    if (i != 0 && ((i % 1000 == 0) || ((i + 1) == updateCnt))) {
                        templateProxy.batchUpdate(updatePoList, true, tableName);
                        updatePoList.clear();
                    }
                }
            }
        }
        // 正常处理
        for (V vo : list) {
            if (vo.getOperateType() == null) {
                return 0;
            }
            // 新增
            if (vo.getOperateType() == 1 && !batchInsert) {
                P po = toPo(vo);
                result += insertSelective(po, tableName);
                vo.setId(po.getId());
            }
            // 修改
            else if (vo.getOperateType() == 2 && !batchUpdate) {
                result += updateByIdSelective(toPo(vo), tableName);
            }
            // 删除
            else if (vo.getOperateType() == 3) {
                result += deleteById(vo.getId(), tableName);
            }
        }
        return result;
    }

    public int insert(BasePO row) throws Exception {
        row.setCreateBase();
        row.setId(MircoIdUtil.generateId());
        int rows = templateProxy.insert(row, true);
        return rows;
    }

    public int insertSelective(BasePO row) throws Exception {
        row.setCreateBase();
        row.setId(MircoIdUtil.generateId());
        int rows = templateProxy.insert(row, false);
        return rows;
    }

    public int insertSelective(BasePO row, String tableName) throws Exception {
        row.setCreateBase();
        row.setId(MircoIdUtil.generateId());
        int rows = templateProxy.insert(row, tableName, false);
        return rows;
    }

    protected int updateSelective(BasePO row, String condition) throws Exception {
        row.setModifyBase();
        return templateProxy.update(row, condition, false, false);
    }

    protected int update(BasePO row, String condition) throws Exception {
        row.setModifyBase();
        return templateProxy.update(row, condition, true, false);
    }

    public int updateByIdSelective(BasePO row) throws Exception {
        return updateByIdSelective(row, null);
    }

    public int updateByIdSelective(BasePO row, String tableName) throws Exception {
        if (row == null || row.getId() == null) {
            throw new SystemException("id 为空 。");
        }
        row.setModifyBase();
        return templateProxy.update(row, null, false, true, tableName);
    }

    public int updateById(BasePO row) throws Exception {
        if (row == null || row.getId() == null) {
            throw new SystemException("id 为空 。");
        }
        row.setModifyBase();
        if (row == null || row.getId() == null) {
            throw new SystemException("id 为空 。");
        }
        return templateProxy.update(row, null, true, true);
    }


}
